using System;
using System.IO;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using UDS.Management.Data;

namespace UDS.Management.Data.Deployment
{
    public class ServerLayoutHelper
    {
#if false
		/// <summary>
		/// A helper class that returns the server name/database name pair information
		/// </summary>
		public class DatabaseServerInfo
		{
			public string ServerName;
			public string DatabaseName;
			public string RoleName;

			public DatabaseServerInfo(string serverName, string roleName, string databaseName)
			{
				this.ServerName = serverName;
				this.RoleName = roleName;
				this.DatabaseName = databaseName;
			}
		}

        private ServerLayoutManager slManager = new ServerLayoutManager();
        private RoleDescriptions roleDescriptions = null;

        /// <summary>
        /// Get the connection string for a server given its name.
        /// </summary>
        /// <param name="serverName">The name of the server or NLB to get the connectionstring for</param>
        /// <returns>The connectionString for the server or NLB requested</returns>
        public string GetServerConnectionString(string serverName)
        {
            string connectionString = "";
            try
            {
                NLB nlb = this.slManager.FindNLB(serverName);
                connectionString = nlb.ConnectionString;
            }
            catch
            {
                Computer computer = this.slManager.FindComputer(serverName);
                connectionString = computer.ConnectionString;
            }
            return connectionString;
        }

		/// <summary>
		/// Get all the database server for the specified publisher database role
		/// </summary>
		/// <exception cref="Exceptions.DatabaseManagementException">Thrown if more than one publisher database is found</exception>
		/// <param name="branchName">The name of the branch to get the publisher database</param>
		/// <param name="databaseRole">The role to get the publisher database for</param>
		/// <returns>The name of the publisher database server</returns>
		public string GetDatabaseServer(string branchName, string publisherDatabase)
		{
			List<string> serverList = new List<string>();

			ServerLayoutBranch branch = slManager.GetBranch(branchName);
			foreach (Zone zone in branch.zones)
			{
				foreach (Computer computer in zone.computers)
				{
					foreach (ComputerRole role in computer.roles)
					{
						if (role.Name == publisherDatabase)
						{
							if (String.IsNullOrEmpty(computer.ConnectionString))
							{
								serverList.Add(computer.Name);
							}
							else
							{
								serverList.Add(computer.ConnectionString);
							}
						}
					}
				}

				foreach (NLB nlb in zone.nlbs)
				{
					foreach (ComputerRole role in nlb.roles)
					{
						if (role.Name == publisherDatabase)
						{
							if (String.IsNullOrEmpty(nlb.ConnectionString))
							{
								serverList.Add(nlb.Name);
							}
							else
							{
								serverList.Add(nlb.ConnectionString);
							}
						}
					}
				}

			}


			if (serverList != null && serverList.Count == 1)
			{
				return serverList[0];
			}
			else
			{
				if (serverList != null && serverList.Count > 1)
				{
					throw new Exceptions.DatabaseManagementException(String.Format("Expected to find 1 server, but found {0} for database role {1} in branch {2}", serverList.Count.ToString(), publisherDatabase, branchName));
				}

				return null;
			}
		}

		public string[] GetServersForRole(string role)
		{
			return slManager.FindRole(role, false);
		}

        /// <summary>
        /// Return only the databases that are not included in a service group
        /// </summary>
        /// <param name="branchName">The name of the branch to look up</param>
        /// <returns></returns>
        public DatabaseServerInfo[] GetDatabases(string branchName)
        {
            string[] groups = slManager.GetGroups(branchName);

            ServerLayoutBranch branch = slManager.GetBranch(branchName);
            GroupDefinition[] gd = branch.groupdefinitions;

            List<DatabaseServerInfo> dbList = new List<DatabaseServerInfo>();

            foreach (Zone z in branch.zones)
            {
                foreach (Computer c in z.computers)
                {
                    bool inServiceGroup = false;

                    foreach (GroupMembership gm in c.groups)
                    {
                        foreach (GroupDefinition group in gd)
                        {
                            if (gm.Name == group.Name)
                            {
                                if (group.Type == "ServiceGroup")
                                {
                                    inServiceGroup = true;
                                    break;
                                }
                            }
                        }

                        if (inServiceGroup) break;
                    }

                    if (!inServiceGroup)
                    {
                        DatabaseRole[] dbs = this.GetDatabaseRoles(c.Name);

                        if (dbs != null)
                        {
                            foreach (DatabaseRole db in dbs)
                            {
								DatabaseServerInfo dbInfo = new DatabaseServerInfo(c.Name, db.Role, db.DatabaseName);
                                dbList.Add(dbInfo);
                            }
                        }
                    }
                }

				foreach (NLB nlb in z.nlbs)
				{
					bool inServiceGroup = false;

					if (nlb.groups.Length > 0)
					{
						foreach (GroupMembership gm in nlb.groups)
						{
							foreach (GroupDefinition group in gd)
							{
								if (gm.Name == group.Name)
								{
									if (group.Type == "ServiceGroup")
									{
										inServiceGroup = true;
										break;
									}
								}
							}

							if (inServiceGroup) break;
						}
					}
				

					if (!inServiceGroup)
					{					
						DatabaseRole[] dbs = this.GetDatabaseRoles(nlb.Name);

						if (dbs != null)
						{
							foreach (DatabaseRole db in dbs)
							{
								DatabaseServerInfo dbInfo = new DatabaseServerInfo(nlb.ConnectionString, db.Role, db.DatabaseName);
								dbList.Add(dbInfo);
							}
						}
					}
				}
            }

            if (dbList != null && dbList.Count > 0)
            {
                return dbList.ToArray();
            }
            else
            {
                return null;
            }
        }

        public DatabaseServerInfo[] GetDatabasesForServiceGroup(string branchName, string serviceGroupName)
        {
            List<DatabaseServerInfo> databaseList = new List<DatabaseServerInfo>();

            foreach (machineRole role in roleDescriptions.Roles.Values)
            {
                if (role.type == roleTypes.database)
                {
                    string[] servers = slManager.FindRoleInGroup(branchName, role.role, serviceGroupName, false);

                    if (servers == null || servers.Length == 0)
                    {
						// Look in the NLBs as well
						servers = slManager.FindRoleInGroup(branchName, role.role, serviceGroupName, true);
                    }

					if (servers != null && servers.Length > 0)
					{
						foreach (string server in servers)
						{
							DatabaseServerInfo dbInfo = new DatabaseServerInfo(server, role.role, this.GetDatabaseName(role));
							databaseList.Add(dbInfo);
						}
					}

                }
            }

            if (databaseList != null && databaseList.Count > 0)
            {
                return databaseList.ToArray();
            }
            else
            {
                return null;
            }
        }            

		/// <summary>
		/// Create an object that exposes various information about the layout of the system.
		/// </summary>
		/// <param name="serverLayoutFileName">The location of the server layout file to use</param>
        public ServerLayoutHelper(string serverLayoutFileName)
        {
            this.slManager.Initialize(new XMLServerLayout(serverLayoutFileName, true), 
                new XMLStringRoleURIs(GetRolesDescription()));
            this.roleDescriptions = this.slManager.GetRoleDescriptions();
        }

		/// <summary>
		/// Get all the branches
		/// </summary>
		/// <returns>The names of all of the branches</returns>
        public string[] GetBranches()
        {
			List<string> branches = new List<string>();
            ServerLayoutBranch[] branchObjects = this.slManager.GetBranches();
            foreach (ServerLayoutBranch branchObject in branchObjects)
            {
                branches.Add(branchObject.Name);
            }
			return branches.ToArray();
        }

        /// <summary>
        /// Gets all database servers in a branch
        /// </summary>
        /// <param name="branchName">The branch to look up the database servers from</param>
        /// <returns>The name of all of the servers that have a database role</returns>
        public string[] GetDatabaseServers(string branchName)
        {
            ArrayList dbServers = new ArrayList();
            foreach (machineRole role in this.roleDescriptions.Roles.Values)
            {
                if (role.type == roleTypes.database)
                {
                    string[] servers = this.slManager.FindRole(branchName, role.role, true);
                    foreach (string server in servers)
                    {
                        if (!dbServers.Contains(server))
                        {
                            dbServers.Add(server);
                        }
                    }
                }
            }
            return (string[])dbServers.ToArray(typeof(string));
        }

        /// <summary>
        /// Gets the list of database roles installed on a server
        /// </summary>
        /// <param name="databaseServerName"></param>
        /// <returns></returns>
        public DatabaseRole[] GetDatabaseRoles(string databaseServerName)
        {
            ComputerRole[] roles = null;
            ArrayList dbRoleList = new ArrayList();
            try
            {
                Computer comp = this.slManager.FindComputer(databaseServerName);
                roles = comp.roles;
            }
            catch{}
            MergeDatabaseRoles(roles, dbRoleList);

            roles = null;
            try
            {
                NLB nlb = this.slManager.FindNLB(databaseServerName);
                roles = nlb.roles;
            }
            catch { }

            MergeDatabaseRoles(roles, dbRoleList);

            return (DatabaseRole[])dbRoleList.ToArray(typeof(DatabaseRole));
        }

		/// <summary>
		/// Returns the first group in the servers group membership.  
		/// </summary>
		/// <param name="serverName">The name of the server</param>
		/// <returns>The name of the first group</returns>
        public string GetServiceGroupName(string serverName)
        {
            try
            {
                Computer computer = this.slManager.FindComputer(serverName);
                GroupMembership[] groups = computer.groups;
                if (groups != null && groups.Length > 0)
                {
                    return groups[0].Name;
                }
            }
            catch { }

            try
            {
                NLB nlb = this.slManager.FindNLB(serverName);
                GroupMembership[] groups = nlb.groups;
                if (groups != null && groups.Length > 0)
                {
                    return groups[0].Name;
                }
            }
            catch { }

            return string.Empty;
        }
#endif

        private string _mainDBConnectionString;

        public ServerLayoutHelper()
        { }

        public ServerLayoutHelper(string mainDbConnectionString)
        {
            _mainDBConnectionString = mainDbConnectionString;
        }

        public Database GetMainDatabase(string databaseName)
        {
            if (string.IsNullOrEmpty(_mainDBConnectionString))
                return null;

            var csBuilder = new System.Data.SqlClient.SqlConnectionStringBuilder(_mainDBConnectionString);
            csBuilder.InitialCatalog = databaseName;
            return new Database(csBuilder);
#if false
                machineRole roleDesc = this.roleDescriptions.Roles[databaseName] as machineRole;
                if (roleDesc != null && roleDesc.type == roleTypes.database)
                {
                    // Get the only resource for the db role
                    string ConnectionString = roleDesc.resource[0].uri;
                    // Need to get rid of the %blah% in the string
                    ConnectionString = System.Text.RegularExpressions.Regex.Replace(ConnectionString, @"\%(.*)\%", "Replaced");
                    System.Data.SqlClient.SqlConnectionStringBuilder Builder = new System.Data.SqlClient.SqlConnectionStringBuilder(ConnectionString);
                    return new Database(servers[0], Builder.InitialCatalog);
                }
            }
            return null;
#endif
        }

#if false
		/// <summary>
		/// Returns all of the service groups for the specified branch
		/// </summary>
		/// <param name="branchName">The branch name to look up the service groups from</param>
		/// <returns>The list of service group objects</returns>
        public Microsoft.Iptv.Server.Management.Data.ServiceGroup[] GetServiceGroups(string branchName)
        {
            string[] groups = this.slManager.GetGroups(branchName, Strings.SERVICE_GROUP_NAME);
            if (groups != null)
            {
                ArrayList sgList = new ArrayList();
                foreach (string group in groups)
                {
                    ServiceGroup sg = new ServiceGroup();
                    sg.Name = group;
                    sgList.Add(sg);
                }

                return (ServiceGroup[])sgList.ToArray(typeof(ServiceGroup));
            }
            else
            {
                return null;
            }
        }

        public static string GetRolesDescription()
        {
			//String resourceName = String.Format(Strings.ROLES_XML_RESOURCE_FORMATTER, Assembly.GetExecutingAssembly().GetName().Name);

			String resourceName = String.Format(Strings.ROLES_XML_RESOURCE_FORMATTER, "Microsoft.Iptv.Server.Management.Data");

            Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);

            using (StreamReader streamReader = new StreamReader(resource, System.Text.Encoding.UTF8, true))
            {
                return streamReader.ReadToEnd();
            }
        }

		private void MergeDatabaseRoles(ComputerRole[] roles, ArrayList dbRoleList)
		{
			if (roles == null)
				return;
			foreach (ComputerRole role in roles)
			{
				machineRole roleDesc = this.roleDescriptions.Roles[role.Name] as machineRole;
				if (roleDesc == null || roleDesc.type != roleTypes.database)
				{
					continue;
				}
				bool found = false;
				foreach (DatabaseRole existingRole in dbRoleList)
				{
					if (string.Compare(existingRole.Role, role.Name, true) == 0)
					{
						found = true;
						break;
					}
				}
				if (!found)
				{
					DatabaseRole dbRole = new DatabaseRole();
					dbRole.Role = roleDesc.role;
					dbRole.Description = roleDesc.description;
					dbRole.DatabaseName = GetDatabaseName(roleDesc);
					dbRoleList.Add(dbRole);
				}
			}
		}

		private string GetDatabaseName(machineRole roleDesc)
		{
			string connectionString = roleDesc.resource[0].uri;
			// Need to get rid of the %blah% in the string
			connectionString = System.Text.RegularExpressions.Regex.Replace(connectionString, @"\%(.*)\%", "Replaced");
			System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder(connectionString);
			return builder.InitialCatalog;
		}
#endif
    }
}
